/*
 * Copyright (C) 2010 Johan Waldeback
 *  
 * This file is part of the OpenFMS project (http://www.openfms-project.org)
 * 
 * OpenFMS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "InputDevice.h"
#include <cmath>
using namespace OpenFms;

/**
 * \class OpenFms::InputDevice
 * \brief Wrapper for a input driver dynamic library.
 *
 * This class is used as a common interface for the core part of
 * OpenFMS to access different kinds of input devices. The InputDevice 
 * class works by loading a dynamic library, called an OpenFMS Input Driver,
 * which contains the code to interface with the actual device. 
 */

/**
 * InputDevice constructor
 */
InputDevice::InputDevice() :
	m_create_fn(NULL), 
	m_destroy_fn(NULL), 
	m_get_number_of_channels_fn(NULL),
	m_capture_fn(NULL),
	m_hDll(NULL),
	m_handle(0)
{
}

/**
 * InputDevice destructor
 */
InputDevice::~InputDevice()
{
	unloadLibrary();
}

/**
 * Load a OpenFMS input driver
 *
 * Loads the specified dynamic library and sets pointers the needed
 * functions in the library.
 *
 * @param filename File name of the dynamic library to load.
 * @return Retrurns true on success, false on fail.
 */
bool InputDevice::loadLibrary( const TCHAR *filename )
{
	// unload anything previously load.
	unloadLibrary();

#ifdef _WIN32
	// load .dll file
	m_hDll = LoadLibrary( filename );
	if( m_hDll == NULL ) return false;

	// find functions
	m_create_fn = reinterpret_cast<InputDevice_create_fn>( GetProcAddress(m_hDll, "create"));
	m_destroy_fn = reinterpret_cast<InputDevice_destroy_fn>( GetProcAddress(m_hDll, "destroy"));
	m_get_number_of_channels_fn = reinterpret_cast<InputDevice_get_number_of_channels_fn>( GetProcAddress(m_hDll, "get_number_of_channels"));
	m_capture_fn = reinterpret_cast<InputDevice_capture_fn>( GetProcAddress(m_hDll, "capture"));

#else // _WIN32
	assert( false && "Non-Windows version not yet implented");
#endif // _WIN32

	return isLoaded();
}

/**
 * Unloads a OpenFMS input driver.
 *
 * Frees all resources allocated in the \c load function
 */
void InputDevice::unloadLibrary()
{
	destroyDevice();

	m_create_fn = NULL;
	m_destroy_fn =  NULL;
	m_get_number_of_channels_fn =  NULL;
	m_capture_fn = NULL;

#ifdef WIN32
	if( m_hDll ) {
		FreeLibrary( m_hDll );
		m_hDll = NULL;
	}
#else // WIN32
	assert( false && "Non-Windows version not yet implanted");
#endif // WIN32
}

/**
 * Returns true if dynamic library is loaded
 *
 * @return Returns true if, and only if, a dynanamic library is currentlt loaded.
 */
bool InputDevice::isLoaded()
{
	return  m_hDll != NULL && m_create_fn != NULL && m_destroy_fn != NULL && m_get_number_of_channels_fn != NULL && m_capture_fn;
}

/** 
 * Calls the \c get_number_of_channels function in the currently loaded library
 * @return Returns whatever \c get_number_of_channels returns.
 */
int InputDevice::getNumberOfChannels()
{
	if( isLoaded() == false ) return false;
	return static_cast<int>(m_destroy_fn(m_handle));
}

/**
 * Calls the \c create function in the currently loaded dynamic library.
 * @note Calls \c destroy before \c create is called to destroy a previously created instance, if any.
 * @return Returns true if \c destroy returns with success, false if not.
 */
bool InputDevice::createDevice()
{
	if( isLoaded() == false ) return false;
	destroyDevice();

	m_handle = m_create_fn();
	return m_handle != 0;
}

/**
 * Calls the \c destroy function in the currently loaded dynamic library
 * @return Returns true if \c destroy returns with success, false if not.
 */
bool InputDevice::destroyDevice()
{
	if( isLoaded() == false ) return false;
	if( m_handle ==  0  ) return false;
	uint64_t handle = m_handle;
	m_handle = 0;
	return m_destroy_fn(handle) == 0;
}

/**
 * Calls the \c capture function in the currently loaded dynamic library.
 * @param p Pointer to an array to be passed on to \c capture.
 * @param start Start value to be passed on to \c capture.
 * @param length Length value to be passed on to \c capture.
 * @return Returns true if \c capture returns with success, false if not.
 */
bool InputDevice::capture( float *p, size_t start, size_t length)
{
	if( m_capture_fn == NULL ) {
		for( size_t i=start; i<(start+length); ++i ) {
			p[i] = 0.f;
		}
		return false;
	}

	return m_capture_fn( m_handle, p, static_cast<uint32_t>(start), static_cast<uint32_t>(length) ) == 0;
}
